#include "sct/mux/tshead.h"
#include "sct/demux/tsdemux.h"

#define SCT_TS_AUDIO_AAC_PID	0X203
#define SCT_TS_VIDEO_AVC_PID	0x202

namespace sct
{

CTsDemux::CTsDemux():mTsStreamID(-1), mCallback(NULL)
{

}

CTsDemux::~CTsDemux()
{
    while(!mPmts.empty())
    {
        CPmt* pmt = mPmts.front();
        mPmts.pop_front();

        while(!pmt->pess.empty())
        {
            CPes* pes = pmt->pess.front();
            pmt->pess.pop_front();

            SCT_DELETE_ARRAY(pes->buf);
            SCT_DELETE(pes);
        }

        SCT_DELETE(pmt);
    }
}

void CTsDemux::parsePAT(const uint8_t * d)
{
    uint8_t * p = (uint8_t *)d;

    MPEGTS_HEADER_T *mpegtsHeader = (MPEGTS_HEADER_T *)p;
    p += sizeof(MPEGTS_HEADER_T);

    if(mpegtsHeader->adaptation_field_control & 0x2)
    {
        ///skip adaption
        uint8_t len = *p;
        p++;

        p += len;
    }

    uint8_t pointer_field = *p;
    p++;

    SCT_ASSERT(0 == pointer_field);

    MPEGTS_PAT_HEADER_T * mpegtsPatHeader = (MPEGTS_PAT_HEADER_T*)(p);
    p += sizeof(MPEGTS_PAT_HEADER_T);

    int streamID = mpegtsPatHeader->transport_stream_id_h << 8 | mpegtsPatHeader->transport_stream_id_l;
    int sectionLen = mpegtsPatHeader->section_length8to11 << 8 | mpegtsPatHeader->section_length0to7;

    mTsStreamID = streamID;

    SCT_ASSERT(0 == mpegtsPatHeader->section_number);
    SCT_ASSERT(0 == mpegtsPatHeader->last_section_number);

    ///to crc
    uint8_t* pEnd = (uint8_t*)mpegtsPatHeader + 3 + sectionLen;

    ///back to program info
    pEnd -= 4;

    while(p < pEnd)
    {
        int program_number = p[0] << 8 | p[1];
        int program_map_PID = (p[2] & 0x1f) << 8 | p[3];

        SCT_ASSERT(0 != program_number);

        addProgram(program_number, program_map_PID);

        p+=4;
    }
}

void CTsDemux::addProgram(const int number, const int pid)
{
    ///check if exists
    std::list<CPmt*>::iterator it;
    for(it = mPmts.begin(); it != mPmts.end(); it++)
    {
        CPmt* pmt = *it;
        if(pmt->number == number)
        {
            return;
        }
    }

    CPmt* item = new CPmt;
    item->number = number;
    item->pid = pid;
    item->pcr = 0;

    mPmts.push_back(item);

    mCallback->onNewProgram(this, item);
}

CTsDemux::CPmt* CTsDemux::checkIfPmt(const int pid)
{
    std::list<CPmt*>::iterator it;

    for(it = mPmts.begin(); it != mPmts.end(); it++)
    {
        CPmt* pmt = *it;
        if(pmt->pid == pid)
        {
            return pmt;
        }
    }

    return NULL;
}

void CTsDemux::parsePMT(const uint8_t *d, CPmt* pmt)
{
    uint8_t * p = (uint8_t *)d;

    MPEGTS_HEADER_T *mpegtsHeader = (MPEGTS_HEADER_T *)p;
    p += sizeof(MPEGTS_HEADER_T);

    if(mpegtsHeader->adaptation_field_control & 0x2)
    {
        ///skip adaption
        uint8_t len = *p;
        p++;

        p += len;
    }

    uint8_t pointer_field = *p;
    p++;

    SCT_ASSERT(0 == pointer_field);

    MPEGTS_PMT_HEADER_T * mpegtsPmtHeader = (MPEGTS_PMT_HEADER_T*)(p);
    p += sizeof(MPEGTS_PMT_HEADER_T);

    int sectionLen = mpegtsPmtHeader->section_length8to11 << 8 | mpegtsPmtHeader->section_length0to7;
    int pcrPid = mpegtsPmtHeader->PCR_PID8to12 << 8 | mpegtsPmtHeader->PCR_PID0to7;
    int programInfoLen = mpegtsPmtHeader->program_info_length8to11 << 8 | mpegtsPmtHeader->program_info_length0to7;

    pmt->pcr_pid = pcrPid;

    ///skip decripe
    p+= programInfoLen;

    ///to crc
    uint8_t* pEnd = (uint8_t*)mpegtsPmtHeader + 3 + sectionLen;
    ///back to stream info
    pEnd -= 4;

    while(p < pEnd)
    {
        MPEGTS_PMT_ELEMENT_T * elem = (MPEGTS_PMT_ELEMENT_T*)(p);
        p+= sizeof(MPEGTS_PMT_ELEMENT_T);

        int streamType = elem->stream_type;
        int streamPid = elem->elementary_PID8to12 << 8 | elem->elementary_PID0to7;
        int esInfo = elem->ES_info_length_h << 8 | elem->ES_info_length_l;

        ///skip esInfo
        p += esInfo;

        addPes(pmt, streamType, streamPid);
    }
}

void CTsDemux::addPes(CPmt*pmt, const int type, const int pid)
{
    std::list<CPes*>::iterator it;
    for(it = pmt->pess.begin(); it != pmt->pess.end(); it++)
    {
        CPes * pes = *it;

        CTSMux::StreamType t = CTSMux::getStreamType(type);

        SCT_ASSERT( (int)(t) != 0 );

        if(pes->type == t)
        {
            return;
        }
    }

    CPes * item = new CPes;
    item->type = CTSMux::getStreamType(type);
    item->buf = NULL;
    item->size = 0;
    item->len = 0;
    item->pid = pid;
    item->pmt = pmt;
    item->pts = 0;
    item->target_len = 0;

    pmt->pess.push_back(item);

    mCallback->onNewPes(this, item);
}

CTsDemux::CPmt* CTsDemux::checkIfPcr(const int pid)
{
    std::list<CPmt*>::iterator it;
    for(it = mPmts.begin(); it != mPmts.end(); it++)
    {
        CPmt* pmt = *it;
        if(pmt->pcr_pid == pid)
        {
            return pmt;
        }
    }

    return NULL;
}

void CTsDemux::parsePcr(const uint8_t *d, CPmt*pmt)
{
    uint8_t * p = (uint8_t *)d;

    MPEGTS_HEADER_T *mpegtsHeader = (MPEGTS_HEADER_T *)p;
    p += sizeof(MPEGTS_HEADER_T);

    if(mpegtsHeader->adaptation_field_control & 0x2)
    {
        MPEGTS_ADAPTATION_FIELD_HEADER_T * head = (MPEGTS_ADAPTATION_FIELD_HEADER_T*)(p);
        p += sizeof(MPEGTS_ADAPTATION_FIELD_HEADER_T);

        if(head->pcr_flag)
        {
            ///got it
            int64_t pcr = 0;

            pcr = p[0];
            pcr = pcr << 8 | p[1];
            pcr = pcr << 8 | p[2];
            pcr = pcr << 8 | p[3];
            pcr = pcr << 1 | p[4]>>7;

            pmt->pcr = pcr;

            mCallback->onPcrUpdate(this, pmt);
        }
    }

}

CTsDemux::CPes* CTsDemux::checkIfPes(const int pid)
{
    std::list<CPmt*>::iterator it;
    for(it = mPmts.begin(); it!=mPmts.end(); it++)
    {
        CPmt* pmt = *it;

        std::list<CPes*>::iterator it2;
        for(it2 = pmt->pess.begin(); it2 != pmt->pess.end(); it2++)
        {
            CPes* pes = *it2;
            if(pes->pid == pid)
            {
                return pes;
            }
        }
    }

    return NULL;
}

void CTsDemux::parsePes(const uint8_t *d, CPes*pes)
{
    uint8_t * p = (uint8_t *)d;

    MPEGTS_HEADER_T *mpegtsHeader = (MPEGTS_HEADER_T *)p;
    p += sizeof(MPEGTS_HEADER_T);

    if(mpegtsHeader->adaptation_field_control & 0x2)
    {
        ///skip adaption
        uint8_t len = *p;
        p++;

        p += len;
    }

    if(mpegtsHeader->payload_unit_start_indicator)
    {
        if(pes->len)
        {
            mCallback->onRecvPesPacket(this, pes);

            if(pes->target_len > 0 && pes->len != pes->target_len)
            {
                SCT_LOGD("pes packet size err:%u %u", pes->len, pes->target_len);
            }

            pes->len = 0;
        }


        MPEGTS_PES_HEADER_T *pesHeader = (MPEGTS_PES_HEADER_T *)p;
        p += sizeof(MPEGTS_PES_HEADER_T);

        if( 0x2 == (pesHeader->pts_dts_flags & 0x2) )
        {
            ///try to get pts dts
            int64_t pts = 0;

            pts = pts << 3 | (p[0] & 0xf)>>1;

            pts = pts << 8 | p[1];
            pts = pts << 7 | p[2]>>1;

            pts = pts << 8 | p[3];
            pts = pts << 7 | p[4]>>1;

            pes->dts = pes->pts = pts;
        }

        if(0x1 == (pesHeader->pts_dts_flags & 0x1))
        {
            int64_t dts = 0;

            dts = dts << 3 | (p[5] & 0xf) >> 1;

            dts = dts << 8 | p[6];
            dts = dts << 7 | p[7]>>1;

            dts = dts << 8 | p[8];
            dts = dts << 7 | p[9]>>1;

            pes->dts = dts;
        }

        p += pesHeader->header_data_length;

        uint32_t size = pesHeader->pes_packet_length_15to8 << 8 | pesHeader->pes_packet_length_7to0;

        pes->target_len = size>0?size - 3 - pesHeader->header_data_length:0;

        if(pes->target_len > 0)
        {
            if(pes->size < pes->target_len)
            {
                SCT_DELETE_ARRAY(pes->buf);

                pes->size = pes->target_len;
                ///fixme: check the value
                pes->buf = new uint8_t[pes->size];
            }
        }
        else
        {
            ///delay to alloc
        }

        pes->len = 0;
    }

    int body_size = 188 - (p-d);

    ///check the free rom
    int left = pes->size - pes->len;
    if(left < body_size)
    {
        int size = pes->size + 1000000 + body_size;
        SCT_LOGD("increase buffer size to:%d", size);

        uint8_t * buf = new uint8_t[size];

        ///move data
        memcpy(buf, pes->buf, pes->len);

        ///update info
        SCT_DELETE_ARRAY(pes->buf);
        pes->buf = buf;
        pes->size = size;
    }

    memcpy(pes->buf + pes->len, p, body_size);

    pes->len += body_size;
}

void CTsDemux::demux(const void * d0)
{
	uint8_t* d = (uint8_t*)d0;
	uint8_t * p = d;

	MPEGTS_HEADER_T *mpegtsHeader = (MPEGTS_HEADER_T *)p;
	p += sizeof(MPEGTS_HEADER_T);

	if(0x47 != mpegtsHeader->sync_byte)
	{
		SCT_LOGD();
		return;
	}

    int pid = mpegtsHeader->pid_12to8 << 8 | mpegtsHeader->pid_7to0;

    if(0 == pid)
    {
        parsePAT(d);

        return;
    }

    CPmt* pmt = checkIfPmt(pid);
    if( pmt )
    {
        parsePMT(d, pmt);

        return;
    }

    pmt = checkIfPcr(pid);
    if( pmt )
    {
        parsePcr(d, pmt);
    }

    CPes* pes = checkIfPes(pid);
    if( pes )
    {
        parsePes(d, pes);
    }
}
}//end namespace
